package org.openzip.openzip.gui.util.selection;

import java.io.File;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import org.openzip.openzip.gui.util.FileUtil;

/**
 * This class serves as a data model holding files which have been selected. It
 * provides flexible way of keeping all selected files from different gui
 * sources at one place.<br>
 * This model is observable, i.e. changes to this model may be observer via a
 * <code>FileSelectionObserver</code> observer.<br>
 * This class implements the <code>Iterator(File)</code>. Therefore this model
 * is iterable for the files in it.
 * 
 * @author Synthetic
 */
public class SelectedFilesModel implements Iterable<File>
{
	//selected files
	private Collection<File> selectedFiles;
	
	/**
	 * Constructor of the model.
	 */
	public SelectedFilesModel() {
		//I chose Set, because we (AFAIC) don't need a single File to be listed
		//twice.
		selectedFiles = new HashSet<File>();
		observers = new HashSet<FileSelectionObserver>();
	}
	
	/**
	 * Adds a single file to selection model
	 * 
	 * @param file
	 *           File to be added.
	 */
	public void addFile(File file) {
		//This gets all children files. Boolean switches inclusion/exclusion
		//of directories.
		//The method getLeafs(File file, boolean includeDirectories) returns
		//collection of all children AND THE PARENT ITSELF!!!
		Collection<File> children = FileUtil.getLeafs(file, true);
		//This removes file entries which are already selected in the model
		children.removeAll(selectedFiles);
		//The rest is now added to the model
		selectedFiles.addAll(children);

		//observers get notified of the change
		notifyObservers(children, true);
	}
	
	/** 
	 * Removes single file received as parameter from selection model 
	 */
	public void removeFile(File file) {
		//recursivelly load all children files and folders
		Collection<File> children = FileUtil.getLeafs(file, true);
		//removes all files that are actually selected
		children.retainAll(selectedFiles);
		//and removes the rest from the model.
		selectedFiles.removeAll(children);
		//observers get notified of the change
		notifyObservers(children, false);
	}
	
	/**
	 * Returns whether the specified file is present in this model.
	 * 
	 * @param file
	 *           Specified file
	 * @return <code>true</code>, if the file is present in the model,
	 *         <code>false</code> otherwise.
	 */
	public boolean isFileSelected(File file) {
		if (selectedFiles.contains(file)) {
			return true;
		}
		return false;
	}
	
	/**
	 * Returns the number of files in this model.
	 */
	public int getFilesCount() {
		return selectedFiles.size();
	}
	
	/**
	 * Returns all selected files.
	 * 
	 * @return All selected files as a <code>Collection</code>.
	 */
	public Collection<File> getSelectedFiles() {
		return selectedFiles;
	}
	
	/**
	 * Iterator for the model.
	 */
	@Override public Iterator<File> iterator(){
		return selectedFiles.iterator();
	}

//=============== Observer pattern implementation methods =================

	//selection observers
	private Set<FileSelectionObserver> observers;
	
	/**
	 * Notifies observers about selection change in the model.
	 * 
	 * @param modified
	 *           Modified files.
	 * @param added
	 *           Whether the files were added or removed (added =
	 *           <code>true</code>, removed = </code>false</code>).
	 */
	private void notifyObservers(Collection<File> modified, boolean added) {
		for (FileSelectionObserver o : observers) {
			o.update(modified, added);
		}
	}
	
	/**
	 * Adds the specified observer to the collection.
	 */
	public void registerObserver(FileSelectionObserver o) {
		observers.add(o);
	}
	
	/**
	 * Removes the specified observer from the collection.
	 */
	public void unregisterObserver(FileSelectionObserver o) {
		observers.remove(o);
	}
	
}
